import random
import re
from pypinyin import lazy_pinyin, Style, pinyin
import datetime
import math


def convert_tones(pinyin):
    """将拼音声调转换成数字"""
    tones1 = []
    for t in pinyin:
        m1 = re.search(r'\d', t)
        if m1:
            tone1 = int(m1.group())
            if tone1 == 0:  # 转换带数字5的轻声
                tone1 = 5
            tones1.append(tone1)
        else:  # 如果没有数字，说明是轻声
            tones1.append(5)
    return tones1

def get_initial_final(hanzi):
    syllable = pinyin(hanzi, style=Style.TONE3)[0][0]  # 使用pypinyin库获取拼音
    initial = syllable[0]  # 声母
    final = syllable[1:]   # 韵母（包括声调）
    tone = convert_tones(hanzi)
    return initial, final, tone

# 1. 统计声母和韵母的频率
def count_initials_finals(filename):
    initial_freq = {}
    final_freq = {}
    tone_freq = {}
    with open(filename, 'r', encoding='utf-8') as file:
        for line in file:
            for char in line:
                if char.isalnum():  # 确保是汉字
                    initial, final, tone = get_initial_final(char)
                    if initial:
                        if initial in initial_freq:
                            initial_freq[initial] += 1
                        else:
                            initial_freq[initial] = 1
                    if final:
                        if final in final_freq:
                            final_freq[final] += 1
                        else:
                            final_freq[final] = 1
                    if tone:
                        if tone in tone_freq:
                            tone_freq[final] += 1
                        else:
                            tone_freq[final] = 1
    return initial_freq, final_freq

# 2. 计算信息量
def calculate_information(initial_freq, final_freq, tone_freq):
    total_chars = sum(initial_freq.values())
    total_chars2 = sum(final_freq.values())

    initial_info = {initial: -math.log2(freq / total_chars) * (freq / total_chars) for initial, freq in initial_freq.items()}
    final_info = {final: -math.log2(freq / total_chars2) * (freq / total_chars2) for final, freq in final_freq.items()}
    # 计算声调的信息量
    tone_info = {}
    total_tones = sum(tone_freq.values())
    for tone, freq in tone_freq.items():
        if freq > 0:  # 确保 freq 是正数
            tone_info[tone] = -math.log2(freq / total_tones) * (freq / total_tones)
        # 如果 freq 为 0，可以选择不将其加入 tone_info 字典，或者赋予一个特定的值
        else:
            tone_info[tone] = 0  # 例如，赋予 0 或其他值
    return initial_info, final_info, tone_info

def count_characters(filename):
    char_freq = {}
    with open(filename, 'r', encoding='utf-8') as file:
        for line in file:
            for char in line:
                if char in char_freq:
                    char_freq[char] += 1
                else:
                    char_freq[char] = 1
    return char_freq

# 2. 计算每个汉字的信息量
def calculate_information_hanzi(char_freq):
    total_chars = sum(char_freq.values())
    information_quantity = {}
    for char, freq in char_freq.items():
        if freq > 0:  # 避免对0取对数
            p = freq / total_chars
            information_quantity[char] = -math.log2(p) * p
    return information_quantity, total_chars

# # 3. 打印汉字频率到文件
# def print_char_frequency_to_file(char_freq, output_filename):
#     with open(output_filename, 'w', encoding='utf-8') as file:
#         for char, freq in sorted(char_freq.items(), key=lambda x: x[1], reverse=True):
#             file.write(f"{char}: {freq}\n")

# 3. 计算成语的信息熵
def calculate_entropy(information_quantity, initial_info, final_info, tone_info, phrase):
    entropy = 0
    for char in phrase:
        if char in information_quantity:
            initial, final,tone = get_initial_final(char)
            entropy += information_quantity[char]+initial_info.get(initial, 0) + final_info.get(final, 0)+ tone_info.get(final, 0)
    return entropy

# 4. 读取成语并计算每个成语的信息熵，然后排序
def calculate_and_sort_entropy(filename, information_quantity, total_chars):
    phrases = []
    with open(filename, 'r', encoding='utf-8') as file:
        for line in file:
            phrase = line.strip()
            phrases.append((calculate_entropy(information_quantity,initial_info, final_info,  phrase), phrase))

    # # 按照信息熵排序
    # sorted_phrases = sorted(phrases, key=lambda x: x[0], reverse=True)
    return phrases
#——————————————————————————————————————————————————————下面是一组用于计算列表信息熵的函数
# 1. 统计声母和韵母,声调的频率
def count_initials_finals2(phrases):
    initial_freq = {}
    final_freq = {}
    tone_freq = {0: 0, 1: 0, 2: 0, 3: 0, 4: 0, 5: 0}
    for phrase in phrases:
        for char in phrase:
            if char.isalnum():  # 确保是汉字
                initial, final, tone = get_initial_final(char)
                if initial:
                    if initial in initial_freq:
                        initial_freq[initial] += 1
                    else:
                        initial_freq[initial] = 1
                if final:
                    if final in final_freq:
                        final_freq[final] += 1
                    else:
                        final_freq[final] = 1
                for tone in tones:  # 统计每个声调的出现次数
                    tone_freq[tone] = tone_freq.get(tone, 0) + 1
    return initial_freq, final_freq, tone_freq
# 4. 读取列表中的成语并计算每个成语的信息熵，然后排序
def calculate_and_sort_entropy2(phrases, information_quantity, initial_info, final_info ,tone_info, total_chars):
    entropy_phrases = []
    for phrase in phrases:
        entropy = calculate_entropy(information_quantity, initial_info, final_info,tone_info, phrase)
        entropy_phrases.append((entropy, phrase))


    return entropy_phrases

def count_characters2(phrases):
    char_freq = {}
    for phrase in phrases:
        for char in phrase:
            if char in char_freq:
                char_freq[char] += 1
            else:
                char_freq[char] = 1
    return char_freq




# 主程序


# 在此处进行待计时的操作
start_time = datetime.datetime.now()

with open('四字成语.txt', 'r', encoding='utf-8') as f:
    data = f.read()
idiom = data.split("\n")
idiom = list(set(idiom))
num_all = 0  # 记录猜谜次数

for games in range(99):  # 进行多次游戏
    print(f"\n成语猜谜第 {games + 1} 回合：")
    ans =random.choice(idiom)  # 选一个成语作为谜底
    print("谜底：", ans)

    out0 = idiom.copy()  # 创建一个成语列表副本，防止修改原列表

    tones = convert_tones(lazy_pinyin(ans, style=Style.TONE2))  # 提取谜底声调数字
    initials = lazy_pinyin(ans, style=Style.INITIALS)  # 提取谜底声母
    finals = lazy_pinyin(ans, style=Style.FINALS)  # 提取谜底韵母

    out = []
    out02 = []
    out002 = []

    out1 = []
    out2 = []
    out3 = []
    out4 = []
    out5 = []
    out6 = []
    out7 = []
    out8 = []

    out9 = []
    out10 = []
    out11 = []
    out12 = []
    out13 = []
    out14 = []
    out15 = []
    out16 = []

    out17= []
    out18 = []
    out19 = []
    out20 = []
    out21 = []
    out22 = []
    out23 = []
    out24 = []





    num = 0  # 猜谜次数
    flag = 1  # 判断是否猜到谜底
    while flag:
        num += 1
        # 猜一个成语

        char_freq = count_characters2(out0)
        initial_freq, final_freq, tone_freq = count_initials_finals2(out0)
        initial_info, final_info, tone_info = calculate_information(initial_freq, final_freq, tone_freq)
        information_quantity, total_chars = calculate_information_hanzi(char_freq)
        sorted_idiom = calculate_and_sort_entropy2(out0, information_quantity,initial_info, final_info, tone_info, total_chars)
        entropies = [item[0] for item in sorted_idiom]  # 存储信息熵
        phrases = [item[1] for item in sorted_idiom]  # 存储成语


        out0 = phrases
        out02 = entropies


        if out0:  # 确保 entropies 非空
            max_entropy = max(out02)
            test = out0[out02.index(max_entropy)]  # 找到对
            out0.remove(test)
            out02.remove(max_entropy)
        else:
            test = random.choice(out0)
            out0.remove(test)  # 从备选中删掉已猜成语

        print(f"第 {num} 次猜：{test}   剩下备选: {len(out0)} 个")
        test_tones = convert_tones(lazy_pinyin(test, style=Style.TONE2))  # 提取猜测成语的声调数字
        test_initials = lazy_pinyin(test, style=Style.INITIALS)
        test_finals = lazy_pinyin(test, style=Style.FINALS)

        for i in range(4):
            # 通过声调筛选
            if test_tones[i] in tones:
                idx = tones.index(test_tones[i])
                if idx == i:
                    for ou in out0:
                        ou_tones = convert_tones(lazy_pinyin(ou, style=Style.TONE2))
                        if ou_tones[idx] == test_tones[i]:
                            out1.append(ou)
                            # out2.append(idiom_entropy_map[ou])
                else:
                    for ou in out0:
                        ou_tones = convert_tones(lazy_pinyin(ou, style=Style.TONE2))
                        if test_tones[i] in ou_tones:
                            out1.append(ou)
                            # out2.append(idiom_entropy_map[ou])
            else:
                for ou in out0:
                    ou_tones = convert_tones(lazy_pinyin(ou, style=Style.TONE2))
                    if test_tones[i] in ou_tones:
                        out9.append(ou)
                        # out10.append(idiom_entropy_map[ou])


            # 通过声母筛选
            if test_initials[i] in initials:
                idx = initials.index(test_initials[i])
                if idx == i:
                    for ou in out0:
                        ou_initials = lazy_pinyin(ou, style=Style.INITIALS)
                        if ou_initials[idx] == test_initials[i]:
                            out3.append(ou)
                            # out4.append(idiom_entropy_map[ou])
                else:
                    for ou in out0:
                        ou_initials = lazy_pinyin(ou, style=Style.INITIALS)
                        if test_initials[i] in ou_initials:
                            out3.append(ou)
                            # out4.append(idiom_entropy_map[ou])
            else:
                for ou in out0:
                    ou_initials = lazy_pinyin(ou, style=Style.INITIALS)
                    if test_initials[i] in ou_initials:
                        out11.append(ou)
                        # out12.append(idiom_entropy_map[ou])


            # 通过韵母筛选
            if test_finals[i] in finals:
                idx = finals.index(test_finals[i])
                if idx == i:
                    for ou in out0:
                        ou_finals = lazy_pinyin(ou, style=Style.FINALS)
                        if ou_finals[idx] == test_finals[i]:
                            out5.append(ou)
                            # out6.append(idiom_entropy_map[ou])
                else:
                    for ou in out0:
                        ou_finals = lazy_pinyin(ou, style=Style.FINALS)
                        if test_finals[i] in ou_finals:
                            out5.append(ou)
                            # out6.append(idiom_entropy_map[ou])
            else:
                for ou in out0:
                    ou_finals = lazy_pinyin(ou, style=Style.FINALS)
                    if test_finals[i] in ou_finals:
                        out13.append(ou)
                        # out14.append(idiom_entropy_map[ou])


            # 通过文字筛选
            if test == ans:  # 如果全字匹配
                flag = 0
                break

            if test[i] in ans:
                for  idx in range(4):
                    if test[i]==ans[idx]:
                        idx1=idx
                if idx1 == i:
                    for ou in out0:
                        if ou[idx1] == test[i]:
                            out7.append(ou)
                            # out8.append(idiom_entropy_map[ou])
                else:
                    for ou in out0:
                        if test[i] in ou and test[i]!=ou[i]:
                            out7.append(ou)
                            # out8.append(idiom_entropy_map[ou])
            else:
                for ou in out0:
                    if test[i] in ou:
                        out15.append(ou)
                        # out16.append(idiom_entropy_map[ou])

            common = [lst for lst, length in zip((out1, out3, out5, out7), map(len, (out1, out3, out5, out7))) if
                      length > 0]
            common3 = [lst for lst, length in zip((out9, out11, out13, out15), map(len, (out9, out11, out13, out15))) if
                      length > 0]

            common_intersection = set.intersection(*map(set, common)) if common else set()
            common3_intersection = set.intersection(*map(set, common3)) if common3 else set()
            common3_list = list(common3_intersection)


            if len(common) == 1:
                out = common[0]
            elif len(common) > 1:
                out = list(common_intersection - common3_intersection)
                if not out0:
                    out = min(common_intersection - common3_intersection, key=len)
            if out:
                out0 = out.copy()  # 不能直接赋值，避免“共享引用”的情况

            out0 = [ou for ou in out0 if ou not in common3_list]

            out1.clear()
            out2.clear()
            out3.clear()
            out4.clear()

            out5.clear()
            out6.clear()
            out7.clear()
            out8.clear()

            out9.clear()
            out10.clear()
            out11.clear()
            out12.clear()

            out13.clear()
            out14.clear()
            out15.clear()
            out16.clear()




    print(f"恭喜你，经过 {num} 次猜测，你已猜中谜底：{test}")
    num_all += num

end_time = datetime.datetime.now()
games += 1
print(f"\n经过 {games + 1} 轮游戏，\n平均每轮猜谜次数：{num_all / games:.1f} 次")
print(f"平均每轮用时：{(end_time - start_time).total_seconds() / games:.2f} 秒")



